﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using GUI.Custom_Classes;
using Tedds_Tool;
using System.ComponentModel;
using System.IO;
using System.Windows.Media;
using System.Windows.Controls;
using System.Media;
using LEET.LEETCommon.Model;
using System.Windows.Threading;
using LEET.Application;
using LEET.CommonInterfaces;
using Compiled_Tests;
using System.Diagnostics;
using LEET.Exceptions.Models;

namespace GUI.Tabs
{
    class ScriptController
    {
        private ScriptViewer Script_Viewer;
        public ListBox ScriptViewerLineNums;
        private string currentScriptName = null;
        Facade f { get; set; }
        IScriptFinder ScriptFinder { get; set; }
        private delegate bool BoolDelegate();

        public ScriptController(ScriptViewer script, Facade f, IScriptFinder finder)
        {
            this.Script_Viewer = script;
            this.f = f;
            this.ScriptFinder = finder;
            ScriptViewer.scriptchanged = false;
        }

        public void CompileTest(object sender, RoutedEventArgs args)
        {
            if (sender != null)
                save_or_load();

            if (!string.IsNullOrEmpty(currentScriptName))
            {
                Dispatcher.CurrentDispatcher.Thread.IsBackground = true;
                Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate
                {
                    Window w = Window.GetWindow(Script_Viewer as ScriptViewer);
                    try
                    {
                        f.CompileAsTest(currentScriptName);
                        if (sender != null)
                            MessageBox.Show(w, "Script compilation completed sucessfully.", "Compilation Completed", MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);
                    }
                    catch (InvalidScriptException e)
                    {
                        //empty script
                        MessageBox.Show(w, e.Message, "Empty Script Error", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
                    }
                    catch (InvalidInstructionException e)
                    {
                        //bad syntax
                        MessageBox.Show(w, "Illegal syntax: " + e.Message, "Syntax Error", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
                    }
                }, DispatcherPriority.Background);
            }
        }

        public void CompileDemo(object sender, RoutedEventArgs e)
        {
            if(sender != null)
                save_or_load();

            if (!string.IsNullOrEmpty(currentScriptName))
            {
                Dispatcher.CurrentDispatcher.Thread.IsBackground = true;
                Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate
                {
                    Window w = Window.GetWindow(Script_Viewer as ScriptViewer);
                    try
                    {
                        f.CompileAsDemo(currentScriptName);
                        if (sender != null)
                            MessageBox.Show(w, "Script compilation completed sucessfully.", "Compilation Completed", MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);
                    }
                    catch (InvalidScriptException)
                    {
                        //empty script
                        MessageBox.Show(w, "Could not compile empty script " + currentScriptName, "Empty Script Error", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
                    }
                    catch (InvalidInstructionException)
                    {
                        //bad syntax
                        MessageBox.Show(w, currentScriptName + " contained illegal syntax.", "Syntax Error", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
                    }
                }, DispatcherPriority.Background);
            }
        }

        public void RunTest(object sender, RoutedEventArgs e)
        {
            save_or_load();
            if (!string.IsNullOrEmpty(currentScriptName))
                Run(f.RunScript);
        }

        public void RunDemo(object sender, RoutedEventArgs e)
        {
            save_or_load();
            if (!string.IsNullOrEmpty(currentScriptName))
                Run(f.RunDemo);
        }

        private delegate LEETTestResult RunCall(String scriptName, String script);
        private void Run(RunCall call)
        {
            RefreshErrorLine(null, null);
            CompileTest(null, null);
            Window w = Window.GetWindow(Script_Viewer as ScriptViewer);

            w.WindowState = WindowState.Minimized;
            Dispatcher.CurrentDispatcher.Thread.IsBackground = true;
            Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate
            {
                StringBuilder script = new StringBuilder();
                foreach (String o in Script_Viewer)
                {
                    script.Append(o).Append("\r\n");
                }
                LEETTestResult testResult = call(currentScriptName, script.ToString());
                
                if(testResult.Failed)
                    Script_Viewer.PaintError(testResult.FailingLine(currentScriptName));
                Script_Viewer.TestFinished();

                w.WindowState = WindowState.Normal;
                w.Focus();
                if (!string.IsNullOrEmpty(testResult.outputmsg))
                    System.Windows.MessageBox.Show(w, testResult.outputmsg, "Test Information", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Asterisk);
                else if (!string.IsNullOrEmpty(testResult.errormsg))
                    System.Windows.MessageBox.Show(w, testResult.errormsg, "Test Run Error", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);

            }, DispatcherPriority.Background);
        }

        /// <summary>
        /// Refreshes the script and removes any red error lines if one was found in a previous run.
        /// </summary>
        public void RefreshErrorLine(object sender, RoutedEventArgs e)
        {
            Script_Viewer.UnPaintErrors();
        }

        public void Save(object sender, RoutedEventArgs args)
        {
            if (ScriptViewer.scriptchanged == false)
                return;

            RefreshErrorLine(null, null);
            ScriptNamerDialog dialog;
            Window owner = Window.GetWindow(Script_Viewer as FrameworkElement);

            do
            {
                if (string.IsNullOrEmpty(currentScriptName))
                    dialog = new ScriptNamerDialog(Window.GetWindow(Script_Viewer as ScriptViewer));
                else
                    dialog = new ScriptNamerDialog(Window.GetWindow(Script_Viewer as ScriptViewer), currentScriptName);

                dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;
                dialog.Owner = owner;
                dialog.ShowDialog();
                currentScriptName = dialog.Return;

                if (dialog.DialogResult == true)
                {
                    if (string.IsNullOrEmpty(currentScriptName))
                    {
                        SystemSounds.Beep.Play();
                        MessageBox.Show("You must specify a name for the script.", "Naming Error", MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.OK);
                    }
                    else
                    {
                        ScriptViewer.scriptchanged = false;
                        bool saveAnyway = true;

                        if (ScriptFinder.ListTestScripts().Contains(currentScriptName))
                            saveAnyway = (bool)owner.Dispatcher.Invoke((BoolDelegate)delegate()
                            {
                                return MessageBox.Show(owner, "File already exists. Do you wish to overwrite?", "Confirmation", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
                            });

                        Script_Viewer.Dispatcher.Thread.IsBackground = true;
                        DispatcherOperation op = Script_Viewer.Dispatcher.BeginInvoke((Action)delegate
                        {
                            if (saveAnyway)
                            {
                                f.SaveScript(currentScriptName, Script_Viewer);
                                if (sender != null)
                                    MessageBox.Show("Script saved saved successfully.", "Save Complete", MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);
                                ScriptViewer.scriptchanged = false;
                            }

                        }, DispatcherPriority.Send);
                    }
                }
                else
                    break;
            } while (string.IsNullOrEmpty(currentScriptName.Trim()));
        }

        public void Load(object sender, RoutedEventArgs args)
        {
            RefreshErrorLine(null, null);
            ScriptSelectorDialog selectorDialog = new ScriptSelectorDialog(Window.GetWindow(Script_Viewer as ScriptViewer), ScriptFinder.ListTestScripts());
            selectorDialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;
            selectorDialog.ShowDialog();
            selectorDialog.Focus();

            String scriptName = selectorDialog.Return;
            currentScriptName = scriptName;

            if (scriptName != null && selectorDialog.DialogResult == true)
            {
                List<string> embeds = new List<string>();
                Script_Viewer.Dispatcher.Thread.IsBackground = true;
                Script_Viewer.Dispatcher.BeginInvoke((Action)delegate
                {
                    IEnumerable<String> script = f.LoadScript(currentScriptName);
                    foreach (String line in script)
                    {
                        if (line.StartsWith("EMBED") && !line.Contains(currentScriptName))
                            embeds.Add(line.Substring(0));
                        Script_Viewer.AddScriptItem(new Event_Pair(line));
                    }
                    ScriptViewer.scriptchanged = false;
                }, DispatcherPriority.Send);
            }
        }

        public void Clear(object sender, RoutedEventArgs args)
        {
            Script_Viewer.Clear();
            currentScriptName = null;
            ScriptViewer.scriptchanged = false;
        }

        private void save_or_load()
        {
            if (Script_Viewer.IsEmpty)
                Load(null, null);
            else if (ScriptViewer.scriptchanged)
                Save(null, null);
        }

        /// <summary>
        /// Returns the embedded scroll object associated with the specified root element.
        /// </summary>
        private DependencyObject GetScrollChild(DependencyObject n)
        {
            bool terminate = false;
            object child = null;
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(n); i++)
            {
                child = VisualTreeHelper.GetChild(n, i);
                if (child is ScrollViewer)
                    return child as DependencyObject;
            }
            if (!terminate)
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(n); i++)
                    child = GetScrollChild(VisualTreeHelper.GetChild(n, i));
            return child as DependencyObject;
        }
    }
}
